/**
 * Anderground project - Subway maps processor application.
 * Copyright (C) 2008-2009 Eldar Abusalimov, Ilya Shurigyn
 * 
 * This file is part of Anderground.
 * 
 * Anderground is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * Anderground is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Anderground.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.anderground.modules.drawer.dynamic;

import java.util.ArrayList;
import java.util.List;

import org.anderground.core.drawer.Calculator;
import org.anderground.core.drawer.Calculator.Mode;
import org.anderground.core.graphics.Canvas;
import org.anderground.core.graphics.Point;
import org.anderground.core.graphics.PointF;
import org.anderground.core.graphics.Rect;
import org.anderground.core.model.Hub;
import org.anderground.core.model.Station;
import org.anderground.core.model.Transition;
import org.anderground.modules.drawer.BaseDrawer;

/**
 * Yet another implementation of {@link BaseDrawer}. The main feature is dynamic
 * merging and splitting of the {@link Hub} components depending on the current
 * zoom without calling prepare method every time. In addition, it has enough
 * speed of preparing to use it in the Editor as real-time preview module.
 * 
 * @see Structure
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class DynamicDrawer extends BaseDrawer {

	/** a structure to draw */
	private Structure structure;
	/** computed bounds of the model by position values */
	private Rect bounds;

	/**
	 * default constructor
	 */
	public DynamicDrawer() {
	}

	@Override
	protected void prepareImpl() {
		Config.drawLabels = this.getMode() == Mode.POSITION;
		this.structure = new Structure(getModelData(), getMode());
		float minX, minY, maxX, maxY;
		float x, y;
		minX = Float.POSITIVE_INFINITY;
		minY = Float.POSITIVE_INFINITY;
		maxX = Float.NEGATIVE_INFINITY;
		maxY = Float.NEGATIVE_INFINITY;

		PointF coordF = new PointF();
		for (Station station : getModelData().getStations()) {
			coordF.set(station, getMode());
			x = coordF.x;
			y = coordF.y;
			if (x < minX) {
				minX = x;
			}
			if (y < minY) {
				minY = y;
			}
			if (x > maxX) {
				maxX = x;
			}
			if (y > maxY) {
				maxY = y;
			}
		}

		this.bounds = new Rect((int) minX, (int) minY, (int) maxX, (int) maxY);

	}

	@Override
	protected void drawImpl(Canvas canvas) {
		Amoeba.setup();
		Bacillus.setup();

		for (Road road : structure.getRoads()) {
			road.draw(canvas);
		}

		for (Crossing crossing : structure.getCrossings()) {
			crossing.draw(canvas);
		}
	}

	@Override
	protected void applyCalculatorImpl(Calculator calculator) {
		structure.applyCalculator(calculator);
	}

	@Override
	protected void selectStationsImpl(Canvas canvas, Station... stations) {
		for (int i = 0; i < stations.length; i++) {
			highlightStation(stations[i], canvas, Config.highlightColor);
		}
	}

	@Override
	protected void selectTransitionsImpl(Canvas canvas,
			Transition... transitions) {
		ArrayList<Amoeba> highlightingAmoebas = new ArrayList<Amoeba>();
		for (Transition transition : transitions) {
			highlightTransition(transition, canvas, Config.wayColor,
					highlightingAmoebas);
		}
		for (Amoeba amoeba : highlightingAmoebas) {
			amoeba.highlight(canvas, Config.wayColor);
		}
	}

	private void highlightTransition(Transition transition, Canvas canvas,
			int color, List<Amoeba> highlightingAmoebas) {
		Bacillus bacillus = structure.getTransitionMapping(transition);
		if (bacillus != null) {
			CompoundBacillus compoundBacillus = bacillus.compoundBacillus;
			while (compoundBacillus != null) {
				if (((CompoundAmoeba) compoundBacillus.evenAmoeba)
						.getCurrentDistance() > Config.mergeDistance) {
					break;
				}
				bacillus = compoundBacillus;
				compoundBacillus = compoundBacillus.compoundBacillus;
			}
			bacillus.highlight(canvas, color);
			highlightingAmoebas.add(bacillus.evenAmoeba);
			highlightingAmoebas.add(bacillus.oddAmoeba);
		}
	}

	private void highlightStation(Station station, Canvas canvas, int color) {
		Amoeba amoeba = structure.getStationMapping(station);
		if (amoeba != null) {
			CompoundAmoeba compoundAmoeba = amoeba.parentAmoeba;
			while (compoundAmoeba != null) {
				if (compoundAmoeba.getCurrentDistance() > Config.mergeDistance) {
					break;
				}
				amoeba = compoundAmoeba;
				compoundAmoeba = compoundAmoeba.parentAmoeba;
			}
			amoeba.highlight(canvas, color);
		}
	}

	@Override
	protected Station[] searchStationsImpl(Point point) {
		structure.getDrawnAmoebas();

		final int DELTA = (int) Config.stationBackRadius;
		float minDist = Float.POSITIVE_INFINITY;
		float dist = 0;

		PointF pointF = new PointF();
		Amoeba nearestAmoeba = null;
		for (Amoeba amoeba : structure.getDrawnAmoebas()) {
			pointF = amoeba.pointF;
			dist = pointF.distanceTo(point);
			if (dist < minDist) {
				minDist = dist;
				nearestAmoeba = amoeba;
			}
		}

		if (nearestAmoeba != null && minDist < DELTA) {
			Station[] stations = new Station[nearestAmoeba.stations.length];
			System.arraycopy(nearestAmoeba.stations, 0, stations, 0,
					nearestAmoeba.stations.length);
			return stations;
		}
		return new Station[0];
	}

	@Override
	protected Rect getBoundsImpl() {
		return bounds;
	}

}
